package com.ea.admin.service.impl;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.ea.admin.mapper.*;
import com.ea.admin.pojo.param.user.UserInfoParam;
import com.ea.admin.pojo.po.*;
import com.ea.admin.pojo.vo.user.UserImportErrorVo;
import com.ea.admin.service.CompanyPermissionService;
import com.ea.admin.service.DeptPermissionService;
import com.ea.admin.service.FileService;
import com.ea.admin.utils.MD5Utils;
import com.ea.common.report.ReportFeign;
import com.ea.common.result.BizException;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

// 有个很重要的点 DemoDataListener 不能被spring管理，要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class UserInfoParamListener extends AnalysisEventListener<UserInfoParam> {

    /**
     * 每隔5条存储数据库，实际使用中可以100条，然后清理list ，方便内存回收
     */
    private static final int BATCH_COUNT = 100;
    /**
     * 缓存的数据
     */
    private final List<UserInfoParam> cachedDataList = new ArrayList<>();
    private final HashMap<String, Long> nameToId = new HashMap<>();
    private final List<UserImportErrorVo> errorList;
    private final AdminOperatingLogMapper adminOperatingLogMapper;
    private final UserInfoMapper userInfoMapper;
    private final CompanyInfoMapper companyInfoMapper;
    private final DeptInfoMapper deptInfoMapper;
    private final PositionInfoMapper positionInfoMapper;
    private final CompanyPermissionService companyPermissionService;
    private final DeptPermissionService deptPermissionService;
    private final FileService fileService;
    private final ReportFeign reportFeign;
    private final Long accountId;
    private final String zipFilePath;

    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     * @param accountId 当前登录管理员的账号
     * @param zipFilePath  压缩文件解压后的路径
     */
    public UserInfoParamListener(List<UserImportErrorVo> errorList, AdminOperatingLogMapper adminOperatingLogMapper, UserInfoMapper userInfoMapper, CompanyInfoMapper companyInfoMapper, DeptInfoMapper deptInfoMapper, PositionInfoMapper positionInfoMapper, CompanyPermissionService companyPermissionService, DeptPermissionService deptPermissionService, FileService fileService, ReportFeign reportFeign, Long accountId, String zipFilePath) {
        this.errorList = errorList;
        this.adminOperatingLogMapper = adminOperatingLogMapper;
        this.userInfoMapper = userInfoMapper;
        this.companyInfoMapper = companyInfoMapper;
        this.deptInfoMapper = deptInfoMapper;
        this.positionInfoMapper = positionInfoMapper;
        this.companyPermissionService = companyPermissionService;
        this.deptPermissionService = deptPermissionService;
        this.fileService = fileService;
        this.reportFeign = reportFeign;
        this.accountId = accountId;
        this.zipFilePath = zipFilePath;
    }

    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        // 直接报错
        throw exception;
    }

    /**
     * 这个每一条数据解析都会来调用
     */
    @Override
    public void invoke(UserInfoParam data, AnalysisContext context) {
        // log.info("解析到一条数据:{}", JSON.toJSONString(data));
        cachedDataList.add(data);
        // 达到BATCH_COUNT了，需要去存储一次数据库，防止数据几万条数据在内存，容易OOM
        if (cachedDataList.size() >= BATCH_COUNT) {
            errorList.addAll(saveData());
            // 存储完成清理 list
            cachedDataList.clear();
        }
    }

    /**
     * 所有数据解析完成了 都会来调用
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据，确保最后遗留的数据也存储到数据库
        errorList.addAll(saveData());
        log.info("所有数据解析完成！");
    }

    /**
     * 加上存储数据库
     */
    private List<UserImportErrorVo> saveData() {
        // log.info("{}条数据，开始存储数据库！", cachedDataList.size());
        List<UserInfo> userInfoList = new ArrayList<>();
        List<UserImportErrorVo> errorList = new ArrayList<>();
        StringBuffer sb = new StringBuffer("批量导入人员");
        cachedDataList.forEach(
                param -> {
                    UserInfo userInfo = new UserInfo();
                    StringBuffer userInfoSb = new StringBuffer();
                    userInfo.setUserId(IdWorker.getId(UserInfo.class));
                    setCompany(param.getCompanyName(), userInfoSb, userInfo);
                    setDept(param.getDeptName(), userInfoSb, userInfo);
                    setPosition(param.getPositionName(), userInfoSb, userInfo);
                    setName(param.getName(), userInfoSb, userInfo);
                    setSex(param.getSex(), userInfoSb, userInfo);
                    setBirthday(param.getBirthday(), userInfoSb, userInfo);
                    setPhoto(param.getPhotoFileName(), userInfoSb, userInfo);
                    if (userInfoSb.length() > 0) {
                        errorList.add(newErrorVo(
                                param,
                                userInfoSb.toString().replaceAll("\n", String.valueOf((char) 10))
                                        .substring(0, userInfoSb.length() - 1))
                        );
                        return;
                    }
                    sb.append("“").append(userInfo.getName()).append("”");
                    userInfo.setIsDel(0);
                    userInfoList.add(userInfo);
                }
        );

        if (!userInfoList.isEmpty()) {
            userInfoMapper.insertBatchSomeColumn(userInfoList);
            // 记录日志
            AdminOperatingLog adminOperatingLog = new AdminOperatingLog();
            adminOperatingLog.setAccountId(accountId);
            adminOperatingLog.setContent(sb.toString());
            adminOperatingLog.setOperatingTime(LocalDateTime.now());
            adminOperatingLogMapper.insert(adminOperatingLog);
        }
        // log.info("存储数据库成功！");
        return errorList;
    }

    private void setCompany(String companyName, StringBuffer sb, UserInfo userInfo) {
        if (companyName == null || companyName.isEmpty()) {
            sb.append("单位名称不能为空；\n");
            return;
        }
        if (!nameToId.containsKey(companyName)) {
            LambdaQueryWrapper<CompanyInfo> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(CompanyInfo::getCompanyName, companyName);
            CompanyInfo companyInfo = companyInfoMapper.selectOne(queryWrapper);
            if (companyInfo == null) {
                sb.append("此单位在系统中不存在；\n");
                return;
            }
            try {
                companyPermissionService.checkCompanyPermission(accountId, companyInfo.getCompanyId());
            } catch (BizException e) {
                sb.append("此单位不在您的权限范围内；\n");
                return;
            }
            nameToId.put(companyName, companyInfo.getCompanyId());
        }

        userInfo.setCompanyId(nameToId.get(companyName));
    }

    private void setDept(String deptName, StringBuffer sb, UserInfo userInfo) {
        if (!nameToId.containsKey(deptName)) {
            LambdaQueryWrapper<DeptInfo> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(DeptInfo::getDeptName, deptName);
            queryWrapper.eq(DeptInfo::getCompanyId, userInfo.getCompanyId());
            DeptInfo deptInfo = deptInfoMapper.selectOne(queryWrapper);
            if (deptInfo == null) {
                sb.append("此部门在目标单位中不存在；\n");
                return;
            }
            try {
                deptPermissionService.checkDeptPermission(accountId, deptInfo.getDeptId());
            } catch (BizException e) {
                sb.append("此部门不在您的权限范围内；\n");
                return;
            }
            nameToId.put(deptName, deptInfo.getDeptId());
        }
        userInfo.setDeptId(nameToId.get(deptName));
    }

    private void setPosition(String positionName, StringBuffer sb, UserInfo userInfo) {
        if (!nameToId.containsKey(positionName)) {
            LambdaQueryWrapper<PositionInfo> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(PositionInfo::getPositionName, positionName);
            queryWrapper.eq(PositionInfo::getCompanyId, userInfo.getCompanyId());
            PositionInfo positionInfo = positionInfoMapper.selectOne(queryWrapper);
            if (positionInfo == null) {
                sb.append("此职位在目标单位中不存在；\n");
                return;
            }
            try {
                companyPermissionService.checkCompanyPermission(accountId, positionInfo.getCompanyId());
            } catch (BizException e) {
                sb.append("此职位不在您的权限范围内；\n");
                return;
            }
            nameToId.put(positionName, positionInfo.getPositionId());
        }
        userInfo.setPositionId(nameToId.get(positionName));
    }

    private void setName(String name, StringBuffer sb, UserInfo userInfo) {
        if (name == null || name.isEmpty()) {
            sb.append("姓名不能为空；\n");
            return;
        }
        userInfo.setName(name);
    }

    private void setSex(String sex, StringBuffer sb, UserInfo userInfo) {
        if (sex == null || sex.isEmpty()) {
            sb.append("性别不能为空；\n");
            return;
        }
        if (!sex.equals("女") && !sex.equals("男")) {
            sb.append("性别只可填写“男”或“女”；\n");
            return;
        }
        userInfo.setSex(sex);
    }

    private void setBirthday(String birthdayStr, StringBuffer sb, UserInfo userInfo) {
        Date birthday;
        if (birthdayStr == null || birthdayStr.isEmpty()) {
            sb.append("生日不能为空；\n");
            return;
        }
        // 将 String 按 yyyy-MM-dd 转换
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            birthday = sdf.parse(birthdayStr);
        } catch (Exception e) {
            sb.append("生日格式不正确；\n");
            return;
        }
        if (birthday.after(new Date())) {
            sb.append("生日不能晚于当前日期；\n");
            return;
        }
        userInfo.setBirthday(birthday);
    }

    private void setPhoto(String photoFileName, StringBuffer sb, UserInfo userInfo) {
        // 从压缩文件中读取照片
        String photoMd5;
        File photo = new File(zipFilePath + File.separator + photoFileName);
        if (photoFileName == null || photoFileName.isEmpty()) {
            sb.append("照片文件名不能为空；\n");
            return;
        }
        if (zipFilePath == null || !photo.exists()) {
            sb.append("压缩文件中，照片名对应的图片不存在；\n");
            return;
        }
        // 计算文件的md5值
        try {
            photoMd5 = MD5Utils.fileToMD5(photo.getPath());
            String type = photo.getName().substring(photo.getName().lastIndexOf(".") + 1);
            // 将文件移动到指定位置
            fileService.saveTempFile(photo, photoMd5, type);
            // 模拟服务接入成功 检查人脸是否合格
            reportFeign.uploadPhoto(photoMd5, userInfo.getUserId().toString(), userInfo.getName());

        } catch (Exception e) {
            sb.append("压缩文件中，照片名对应的图片读取失败；\n");
            return;
        }
        userInfo.setPhoto(photoMd5);
    }


    private UserImportErrorVo newErrorVo(UserInfoParam param, String msg) {
        UserImportErrorVo errorVo = new UserImportErrorVo();
        errorVo.setName(param.getName());
        errorVo.setCompanyName(param.getCompanyName());
        errorVo.setDeptName(param.getDeptName());
        errorVo.setPositionName(param.getPositionName());
        errorVo.setSex(param.getSex());
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date date = sdf.parse(param.getBirthday());
            SimpleDateFormat to = new SimpleDateFormat("yyyy-MM-dd");
            errorVo.setBirthday(to.format(date));
        } catch (Exception e) {
            errorVo.setBirthday(param.getBirthday());
        }
        errorVo.setPhotoFileName(param.getPhotoFileName());
        errorVo.setErrorMsg(msg);
        return errorVo;
    }
}
